package com.cong.security.core.code.image;

import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.image.BufferedImage;
import java.util.Random;
import com.cong.security.core.code.ValidateCodeGenerator;
import com.cong.security.core.properties.SecurityProperties;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

@Component("imageCodeGenerator")
public class ImageCodeGenerator implements ValidateCodeGenerator {

	private static char code[] = "abcdefghijkmnpqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ23456789".toCharArray();

	@Autowired
	private SecurityProperties securityProperties;

	@Override
	public ImageCode generate() {
		// 在内存中创建图象
		BufferedImage image = new BufferedImage(securityProperties.getCode().getImage().getWidth(),
				securityProperties.getCode().getImage().getHeight(), BufferedImage.TYPE_INT_RGB);
		// 获取图形上下文
		Graphics g = image.getGraphics();
		// 生成随机类
		Random random = new Random();
		// 设定背景色
		g.setColor(getRandColor(200, 250));
		g.fillRect(0, 0, securityProperties.getCode().getImage().getWidth(),
				securityProperties.getCode().getImage().getHeight());
		// 设定字体
		g.setFont(new Font("Times New Roman", Font.PLAIN, 18));
		// 设置颜色
		g.setColor(getRandColor(160, 200));
		// 随机产生100条干扰线，使图象中的认证码不易被其它程序探测到
		for (int i = 0; i < 100; i++) {
			int x = random.nextInt(securityProperties.getCode().getImage().getWidth());
			int y = random.nextInt(securityProperties.getCode().getImage().getHeight());
			int xl = random.nextInt(12);
			int yl = random.nextInt(12);
			g.drawLine(x, y, x + xl, y + yl);
		}
		// 取随机产生的认证码(4位数字)
		String sRand = "";
		for (int i = 0; i < securityProperties.getCode().getImage().getLength(); i++) {
			String rand = String.valueOf(code[random.nextInt(code.length)]);
			sRand += rand;
			// 将认证码显示到图象中
			g.setColor(new Color(20 + random.nextInt(110), 20 + random.nextInt(110), 20 + random.nextInt(110)));
			// 调用函数出来的颜色相同，可能是因为种子太接近，所以只能直接生成
			g.drawString(rand, 13 * i + random.nextInt(6), random.nextInt(7) + 15);
		}
		// shear(g, securityProperties.getCode().getImage().getWidth(),
		// securityProperties.getCode().getImage().getHeight(), getRandColor(200, 200));// 使图片扭曲
		// 赋值验证码
		// 图象生效
		g.dispose();
		ImageCode imageCode = new ImageCode(image, sRand, securityProperties.getCode().getImage().getExpireIn());
		return imageCode;
	}

	/*
	 * 给定范围获得随机颜色
	 */
	private static Color getRandColor(int fc, int bc) {
		Random random = new Random();
		if (fc > 255)
			fc = 255;
		if (bc > 255)
			bc = 255;
		int r = fc + random.nextInt(bc - fc);
		int g = fc + random.nextInt(bc - fc);
		int b = fc + random.nextInt(bc - fc);
		return new Color(r, g, b);
	}

	/**
	 * 使图形扭曲
	 * 
	 * @param g
	 * @param w1
	 * @param h1
	 * @param color
	 * @author single-聪
	 * @date 2019年11月12日
	 * @version 1.0.1
	 */
	private static void shear(Graphics g, int w1, int h1, Color color) {
		shearX(g, w1, h1, color);
		shearY(g, w1, h1, color);
	}

	/**
	 * 扭曲
	 * 
	 * @param g
	 * @param w1
	 * @param h1
	 * @param color
	 * @author single-聪
	 * @date 2019年11月12日
	 * @version 1.0.1
	 */
	private static void shearX(Graphics g, int w1, int h1, Color color) {
		Random random = new Random();
		int period = random.nextInt(2);
		boolean borderGap = true;
		int frames = 1;
		int phase = random.nextInt(2);
		for (int i = 0; i < h1; i++) {
			double d = (double) (period >> 1)
					* Math.sin((double) i / (double) period + (6.2831853071795862D * (double) phase) / (double) frames);
			g.copyArea(0, i, w1, 1, (int) d, 0);
			if (borderGap) {
				g.setColor(color);
				g.drawLine((int) d, i, 0, i);
				g.drawLine((int) d + w1, i, w1, i);
			}
		}
	}

	/**
	 * 扭曲
	 * 
	 * @param g
	 * @param w1
	 * @param h1
	 * @param color
	 * @author single-聪
	 * @date 2019年11月12日
	 * @version 1.0.1
	 */
	private static void shearY(Graphics g, int w1, int h1, Color color) {
		Random random = new Random();
		int period = random.nextInt(40) + 10; // 50;
		boolean borderGap = true;
		int frames = 20;
		int phase = 7;
		for (int i = 0; i < w1; i++) {
			double d = (double) (period >> 1)
					* Math.sin((double) i / (double) period + (6.2831853071795862D * (double) phase) / (double) frames);
			g.copyArea(i, 0, 1, h1, 0, (int) d);
			if (borderGap) {
				g.setColor(color);
				g.drawLine(i, (int) d, i, 0);
				g.drawLine(i, (int) d + h1, i, h1);
			}
		}
	}

	public SecurityProperties getSecurityProperties() {
		return securityProperties;
	}

	public void setSecurityProperties(SecurityProperties securityProperties) {
		this.securityProperties = securityProperties;
	}
}
